#include "qbddmanager.h"
#include "qfilecachedmemory.h"

QBDDManager* QBDDManager::m_instance = NULL;
bool QBDDManager::m_valid = true;
QBDDManager::QBDDManager(QString dbFile)
{
	#ifdef   LOG_ACTIVATED
	__TRACE__
	#endif
	#ifdef   LEAK_ACTIVATED
	__LEAK__
	#endif
	QFileInfo infos( dbFile );
	if(infos.exists())
	{
		destroyBdd();
		createBdd(dbFile);
		openBdd();
		listPlp();
	}
	else
		m_valid = false;
}

QBDDManager::~QBDDManager()
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
}


QBDDManager* QBDDManager::instance(QString dbFile)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   if(NULL == m_instance)
   {
      m_instance = new QBDDManager(dbFile);
   }
   return m_instance;
}

void QBDDManager::destroy()
{   
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   if(NULL != m_instance)
   {
      delete m_instance;
      m_instance = NULL;
   }
}

bool QBDDManager::reinit(QString dbFile)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   destroy();
   instance(dbFile);
   return isValid();
}
   
void QBDDManager::createBdd(QString dbFile)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QBDDFile file(dbFile);
   QStringList data = file.current();
   m_identifier = 0;
   QDir dir(DATABASE_DIRECTORY);
   if(!dir.exists())
      dir.mkdir(dir.absolutePath());

   QStringList cat1, cat2, cat3, cat4;
   while(data.size())
   {
      QBDDItem* item = __extract(file.header(),data);

      if(item)
      {
         if(!m_bdd.contains(item->bddFile()))
         {
            Db* db = new Db(NULL,DB_CXX_NO_EXCEPTIONS);
            m_bdd[item->bddFile()] = db;
            QString completeName = DATABASE_DIRECTORY + QString("\\") + item->bddFile() + DATABASE_EXTENSION;
            u_int32_t flags = DB_EXCL|DB_CREATE;
            int result = db->open(NULL,completeName.toAscii().data(),NULL,DB_RECNO,flags,0);
            if( 0 != result)
               report(QString("Erreur ( code: %1 ) de creation de la base %2").arg(result).arg(completeName));
         }

         Db* database = m_bdd[item->bddFile()];
         QByteArray rawData = item->toBdd();
         Dbt key(&m_identifier,sizeof(quint32));
         Dbt data(rawData.data(),rawData.size());

         if(0 != database->put(NULL,&key,&data,NULL))
            report(QString("Erreur d'insertion de l'identifieur %1 dans la base %2").arg(m_identifier).arg(item->bddFile()));
         else
            report(QString("Creation de l'identifieur %1 dans la base %2").arg(m_identifier).arg(item->bddFile()));

		 addTo(item->get(BDD_CAT_1),cat1);
		 addTo(item->get(BDD_CAT_2),cat2);
		 addTo(item->get(BDD_CAT_3),cat3);
		 addTo(item->get(BDD_CAT_4),cat4);
         m_identifier ++;
      }

      file.next();
      data = file.current();
   }
   closeBdd();
   
   QFileCachedMemory memory;
   memory.add(FILE_CACHE_BDD_CAT_1,cat1);
   memory.add(FILE_CACHE_BDD_CAT_2,cat2);
   memory.add(FILE_CACHE_BDD_CAT_3,cat3);
   memory.add(FILE_CACHE_BDD_CAT_4,cat4);
}
   
void QBDDManager::destroyBdd()
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QHash<QString,Db*>::iterator it;

   for(it = m_bdd.begin();it != m_bdd.end() ; it++ )
   {
      Db* db = it.value(); 
      if(db)
      {
         delete db;
         m_bdd.remove(it.key());
      }
   }

   QStringList filter;
   filter << "*.*";

   QDir dir(DATABASE_DIRECTORY);
   if(dir.exists())
   {
      QStringList filesToDestroy = dir.entryList(filter,QDir::Files|QDir::NoDotAndDotDot);

      foreach(QString name, filesToDestroy )
      {
         QFile file(name);
         file.remove();
      }
   }
}
   
void QBDDManager::openBdd()
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QStringList filter;
   filter << QString("*")+DATABASE_EXTENSION;

   u_int32_t flags = DB_RDONLY;

   QDir dir(DATABASE_DIRECTORY);
   QString path = dir.absolutePath();
   if(dir.exists())
   {
      QStringList bdd = dir.entryList(filter,QDir::Files|QDir::NoDotAndDotDot);
      foreach(QString file, bdd)
      {
         QString name = file;
         QString completeName = DATABASE_DIRECTORY + QString("\\") +file;
         name.remove(DATABASE_EXTENSION);
         Db* db = new Db(NULL,DB_CXX_NO_EXCEPTIONS);
         m_bdd[name] = db;
         int result = db->open(NULL,completeName.toAscii().data(),NULL,DB_RECNO,flags,0);
         if( 0 != result)
            report(QString("Erreur( code: %1 ) d\'ouverture de la base %2").arg(result).arg(completeName));

      }
   }
}

void QBDDManager::closeBdd()
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QHash<QString,Db*>::iterator it;

   for(it = m_bdd.begin();it != m_bdd.end() ; it++ )
   {
      Db* db = it.value(); 
      if(db)
         db->close(0);
   }
}

void QBDDManager::report(QString report)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QString date = QDateTime::currentDateTime().toString("[dd|MM|yyyy][hh:mm:ss] "); 
   QFile file(DATABASE_REPORT);
   if(file.open(QIODevice::Append|QIODevice::Text|QIODevice::WriteOnly))
   {
      QTextStream out(&file);
      out << date+report+"\n";
      file.close();
   }
}

void QBDDManager::report(QStringList report)
{   
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QString date = QDateTime::currentDateTime().toString("[dd|MM|yyyy][hh:mm:ss] "); 
   QFile file(DATABASE_REPORT);
   if(file.open(QIODevice::Append|QIODevice::Text|QIODevice::WriteOnly))
   {
      QTextStream out(&file);
      foreach(QString data, report)
      {
         out << date+ data +"\n";
      }
      file.close();
   }
}

template <class T>
QList<T*> QBDDManager::searchSpecific(const QList<QStringList>& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QList<T*> result;
   
   if(m_bdd.contains(T::dbFile()))
   {
      Db* database = m_bdd[T::dbFile()];
      if(database)
      {
         Dbc* cursor;
         database->cursor(NULL,&cursor,0);
         if(cursor)
         {
            Dbt key, data;
            while(cursor->get(&key,&data,DB_NEXT) == 0)
            {
               QByteArray recovery((char*) data.get_data(),data.get_size());
               T* item = new T(recovery);
               quint32 score = item->score(terms);
               if(score != 0)
                  result << item;
               else
                  delete item;
            }
            cursor->close();

      qSort(result);
         }
      }
   }

   QList<T*> del;
   if(result.size() > maxTerms && maxTerms != 0)
     del = result.mid(maxTerms);

   for(int i=0;i<del.size();i++)
      delete del.at(i);

  if(maxTerms)
   return result.mid(0,maxTerms);
  else
    return result;
}



QList<QResistorItem*> QBDDManager::searchR(const QList<QStringList>& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   return searchSpecific<QResistorItem>(terms,maxTerms);
}

QList<QCapacitorItem*> QBDDManager::searchC(const QList<QStringList>& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   return searchSpecific<QCapacitorItem>(terms,maxTerms);
}

QList<QLedItem*> QBDDManager::searchLed(const QList<QStringList>& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   return searchSpecific<QLedItem>(terms,maxTerms);
}

QList<QSelfItem*> QBDDManager::searchL(const QList<QStringList>& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   return searchSpecific<QSelfItem>(terms,maxTerms);
}


QList<QBDDItem*> QBDDManager::searchOther(const QList<QStringList>& terms,const quint8& maxTerms)
{
  #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   return searchSpecific<QBDDItem>(terms,maxTerms);
}
QList<QBDDItem*> QBDDManager::searchAll(const QStringList& terms,const quint8& maxTerms)
{
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QList<QBDDItem*> result;
   QHash<QString,Db*>::iterator it;
   for(it = m_bdd.begin() ; it != m_bdd.end() ; it++)
   {
    Db* database = it.value();
    if(database)
    {
      Dbc* cursor;
      database->cursor(NULL,&cursor,0);
      if(cursor)
      {
        Dbt key, data;
        while(cursor->get(&key,&data,DB_NEXT) == 0)
        {
          QByteArray recovery((char*) data.get_data(),data.get_size());
          QBDDItem* item = new QBDDItem(recovery);
          if(item)
          {
            foreach(QString search,terms)
            {
              if(item->contains(search))
              {
                result << item;
                break;
              }
              else
                delete item; 
            }                  
          }
        }
        cursor->close();
      }
    }
   }
   return result;
}



void QBDDManager::listPlp()
{   
   #ifdef   LOG_ACTIVATED
   __TRACE__
   #endif
   #ifdef   LEAK_ACTIVATED
   __LEAK__
   #endif
   QHash<QString,Db*>::iterator it;

   for(it = m_bdd.begin();it != m_bdd.end() ; it++ )
   {
      Db* database = it.value();
      if(database)
      {
         Dbc* cursor;
         database->cursor(NULL,&cursor,0);
         if(cursor)
         {
            Dbt key, data;
      QStringList plps,manu,supplier;
            while(cursor->get(&key,&data,DB_NEXT) == 0)
            {
               QByteArray recovery((char*) data.get_data(),data.get_size());
               QBDDItem item(recovery);
               plps << item.get(BDD_PLP);
        manu << item.get(BDD_FAB);
        supplier << item.get(BDD_FOURNISSEUR);
            }
      QFileCachedMemory file;
      file.add(FILE_CACHE_BDD_PLP,plps);
      file.add(FILE_CACHE_BDD_MANU,manu);
      file.add(FILE_CACHE_BDD_FOURNISSEUR,supplier);
            cursor->close();
         }
      }
   }
}
